package id.co.rho.scoring.cid.framework.math.evaluator.model.visitor;

import id.co.rho.scoring.cid.framework.math.evaluator.model.BinaryOpValueNode;
import id.co.rho.scoring.cid.framework.math.evaluator.model.LeafValueNode;
import id.co.rho.scoring.cid.framework.math.evaluator.model.MEValueNode;
import id.co.rho.scoring.cid.framework.math.evaluator.model.UnaryOpValueNode;

import java.math.BigDecimal;
import java.util.HashMap;

/**
 * Created by IntelliJ IDEA.
 * User: bungrudi
 * Date: 9/14/11
 * Time: 3:53 PM
 */
public class NodeWalkerExecutor {
//    private static final BinaryNodeWalker BINARY_NODE_WALKER = new BinaryNodeWalker();
//    private static final UnaryNodeWalker UNARY_NODE_WALKER = new UnaryNodeWalker();
//    private static final LeafNodeWalker LEAF_NODE_WALKER = new LeafNodeWalker();

    private static final AddEvaluator ADD_CALCULATOR = new AddEvaluator();
    private static final SubCalculation SUB_CALCULATOR = new SubCalculation();
    private static final TimeEvaluator TIME_CALCULATOR = new TimeEvaluator();
    private static final DivideEvaluator DIVIDE_CALCULATOR = new DivideEvaluator();
    private static final ModEvaluator MOD_CALCULATOR = new ModEvaluator();
    private static final NegationEvaluator NEGATION_CALCULATOR = new NegationEvaluator();
    private static final ValueEvaluator VALUE_CALCULATOR = new ValueEvaluator();
//    private static final VariableCalculator VARIABLE_CALCULATOR;
    private final VariableEvaluator variableCalculator;

    // khusus untuk VariableCalculator, di createnya per context (per CalculationModel).
    // we need to abstract this away.


    public NodeWalkerExecutor(VariableEvaluator variableCalculator) {
        this.variableCalculator = variableCalculator;
    }

    private final HashMap<MEValueNode, BigDecimal> resultMap = new HashMap<MEValueNode, BigDecimal>();

    public void walk(MEValueNode node) {
//        NodeWalker walker = getWalker(node);
//        Set<MEValueNode> children = walker.getChildren(node);
        MEValueNode[] children = node.getChildren();
        if (children!=null) for(MEValueNode child : children) {
            walk(child);
        }

        NodeEvaluator calcExec = getCalculator(node);
        BigDecimal result = calcExec.calculate(node,this);
        resultMap.put(node,result);
    }

    private NodeEvaluator getCalculator(MEValueNode node) {
        if(node instanceof BinaryOpValueNode) {
            BinaryOpValueNode v = (BinaryOpValueNode) node;
            switch(v.getOperator()) {
                case ADD:
                    return ADD_CALCULATOR;
                case SUB:
                    return SUB_CALCULATOR;
                case MULT:
                    return TIME_CALCULATOR;
                case DIV:
                    return DIVIDE_CALCULATOR;
                case MOD:
                    return MOD_CALCULATOR;
                default:
                    throw new RuntimeException("no calculator available for "+v.getOperator());
            }
        } else if(node instanceof UnaryOpValueNode) {
            return NEGATION_CALCULATOR;
        } else if(node instanceof LeafValueNode) {
            LeafValueNode v = (LeafValueNode) node;
            if(v.getValue()!=null) {
                return VALUE_CALCULATOR;
            } else {
                return variableCalculator;
            }
        }
        throw new RuntimeException("no walker available for "+node.getClass());
    }

//    private NodeWalker getWalker(MEValueNode node) {
//        if(node instanceof BinaryOpValueNode) {
//            return BINARY_NODE_WALKER;
//        } else if(node instanceof UnaryOpValueNode) {
//            return UNARY_NODE_WALKER;
//        } else if(node instanceof LeafValueNode) {
//            return LEAF_NODE_WALKER;
//        }
//        throw new RuntimeException("no walker available for "+node.getClass());
//    }

    public HashMap<MEValueNode, BigDecimal> getResultMap() {
        return resultMap;
    }
}
